package cnet

import (
	"fmt"
	"github.com/cmajor/cherry/iface"
	"github.com/cmajor/cherry/utils"
	"net"
	"sync"
	"sync/atomic"
	"time"
)

//todo ctrl+c 优雅退出
type TCPServer struct {
	name string
	host string
	port string

	cid uint32 //todo 如果cid达到最大之后怎么办？

	ln *net.TCPListener
	//解析方法
	parser iface.IParser

	//管理连接
	connGrp  sync.WaitGroup
	//todo 这里考虑使用 sync.Map或者mutex+map
	connSet  sync.Map//map[uint32]*TCPConn //key=uint32 value=TCPConn
	connNum   uint32

	//模块&层之间通知chan
	outerBridge    chan *iface.InternalMsg
	internalBridge chan *iface.InternalMsg
}

func (s *TCPServer) Init(name, host, port string, c chan *iface.InternalMsg) {
	s.host = host
	s.name = name
	s.port = port
	s.outerBridge = c
	s.internalBridge = make(chan *iface.InternalMsg, utils.MaxPendingSize())
	//s.connSet = make(map[uint32]*TCPConn)
}

func (s *TCPServer) Start() {
	fmt.Printf("[Cherry] Server Name:%s,listen at host:%s,port:%s\n", s.name, s.host, s.port)
	fmt.Printf("[Cherry] Version v0.5,MaxConn:%d,MaxPackageSize:%d\n",
		utils.GlobalConfig.MaxConn,
		utils.GlobalConfig.MaxPackageSize)
	tcpSock, err := net.ResolveTCPAddr("tcp", s.host+":"+s.port)
	if err != nil {
		fmt.Println("[Cherry] resolve tcp host error", err)
		panic(err)
	}
	s.ln, err = net.ListenTCP("tcp", tcpSock)
	if err != nil {
		fmt.Println("[Cherry] listen tcp host error", err)
		panic(err)
	}
	fmt.Printf("[Start] Server Listenner at IP:%s,Port %s, is serving\n", s.host, s.port)

	go s.handleInnerCommand()
	for {
		conn, err := s.ln.AcceptTCP() //阻塞等待
		if err != nil {
			//如果遇到了网络错误,睡眠几秒后再accept
			if ne, ok := err.(net.Error); ok && ne.Temporary() {
				time.Sleep(5 * time.Second)
				fmt.Println("[Cherry] Start().AcceptTCP error = ", err, " sleep 5 sec")
				continue
			}
			//如果不是网络错误,直接退出
			fmt.Println("[Cherry] Start().AcceptTCP error = ", err, " exit loop")
			return
		}

		if s.isOverLimit() {
			fmt.Println("[Cherry] conn num is max")
			_ = conn.Close()
			continue
		}

		//添加管理
		iConn := s.addConn(conn)
		s.outerBridge <- &iface.InternalMsg{SysID: iface.NOTIFY_TCP_ESTABLISH, ConnID: iConn.ConnID()}

		go func() {
			iConn.Run() //阻塞,直到出错或关闭
			iConn.Close()
			s.outerBridge <- &iface.InternalMsg{MsgID: iface.NOTIFY_TCP_CLOSE, ConnID: iConn.ConnID()}
			fmt.Println("[Cherry] ConnID = ", iConn.ConnID(), " is exiting")
			//清理资源
			//delete(s.connSet, iConn.ConnID())
			s.connSet.Delete(iConn.ConnID())
			atomic.AddUint32(&s.connNum,-1)
			//fmt.Println("[Cherry] TCPServer after conn id", iConn.ConnID(), " exit have ", len(s.connSet), " conns")
			fmt.Println("[Cherry] TCPServer after conn id", iConn.ConnID(), " exit have ",atomic.LoadUint32(&s.connNum)," conns")
			s.connGrp.Done()
		}()
	}
}

func (s *TCPServer) SendMsg(msg *iface.InternalMsg) {
	if len(s.internalBridge) >= cap(s.internalBridge) {
		fmt.Println("[Cherry] TCPServer internalBridge is full")
		return
	}
	s.internalBridge <- msg
}

func (s *TCPServer) handleInnerCommand() {
	//处理内部请求
	fmt.Println("[CherryServer] start handleInnerCommand")
	for imsg := range s.internalBridge {
		if imsg.SysID < 0 {
			break
		}
		if imsg.SysID < iface.COMMAND_START && imsg.SysID > iface.COMMAND_END {
			break
		}
		s.handleCommand(imsg)
	}
	fmt.Println("[CherryServer] start handleInnerCommand")
}
func (s *TCPServer) handleCommand(msg *iface.InternalMsg) {
	switch msg.SysID {
	case iface.COMMAND_WRITE:
		if c, exist := s.connSet.Load(msg.ConnID); exist {
			if v,ok := c.(*TCPConn);ok{
				v.SendCustom(c)
			}
		}
	case iface.COMMAND_CLOSE:
		if c, exist := s.connSet.Load(msg.ConnID); exist {
			if v,ok := c.(*TCPConn);ok{
				v.Close()
			}
		}
	}
}

func (s *TCPServer) Stop() {
	s.outerBridge <- &iface.InternalMsg{SysID: -1}
	close(s.outerBridge)

	s.internalBridge <- &iface.InternalMsg{SysID: -1}
	close(s.internalBridge)

	_ = s.ln.Close()
	s.connSet.Range(func(key, value interface{}) bool {
		if cc,ok := value.(*TCPConn);ok{
			cc.Close()
			fmt.Println("TCPServer Stop close connID = ",key)
		}
		return true
	})
	s.connGrp.Wait()
}

func (s *TCPServer) isOverLimit() bool {
	return atomic.LoadUint32(&s.connNum) >= uint32(utils.GlobalConfig.MaxConn)
}

func (s *TCPServer) addConn(conn *net.TCPConn) *TCPConn {
	//创建连接
	iConn := NewTCPConn(conn, s.cid, s.outerBridge)
	//s.connLock.Lock()
	s.connSet.Store(s.cid,iConn)
	//s.connLock.Unlock()
	s.cid++
	atomic.AddUint32(&s.connNum,1)
	s.connGrp.Add(1)
	fmt.Println("after add conn id = ", iConn.ConnID(), " TCPServer have ", atomic.LoadUint32(&s.connNum), " conns")
	return iConn
}
